﻿//using UnityEngine;
//using System.Collections;
//using System.Collections.Generic;
//using System.IO;
//using ILRuntime.CLR.TypeSystem;
//using ILRuntime.CLR.Method;
//using ILRuntime.CLR.Utils;
//using ILRuntime.Runtime.Intepreter;
//using ILRuntime.Runtime.Stack;
//using ILRuntime.Runtime.Enviorment;
////下面这行为了取消使用WWW的警告，Unity2018以后推荐使用UnityWebRequest，处于兼容性考虑Demo依然使用WWW
//#pragma warning disable CS0618

//public class MonoBehaviourDemo : MonoBehaviour
//{
//    static MonoBehaviourDemo instance;
//    System.IO.MemoryStream fs;
//    System.IO.MemoryStream p;
//    public static MonoBehaviourDemo Instance
//    {
//        get { return instance; }
//    }
//    //AppDomain是ILRuntime的入口，最好是在一个单例类中保存，整个游戏全局就一个，这里为了示例方便，每个例子里面都单独做了一个
//    //大家在正式项目中请全局只创建一个AppDomain
//    AppDomain appdomain;

//    void Start()
//    {
//        instance = this;
//        StartCoroutine(LoadHotFixAssembly());
//    }

//    IEnumerator LoadHotFixAssembly()
//    {
//        //首先实例化ILRuntime的AppDomain，AppDomain是一个应用程序域，每个AppDomain都是一个独立的沙盒
//        appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
//        //正常项目中应该是自行从其他地方下载dll，或者打包在AssetBundle中读取，平时开发以及为了演示方便直接从StreammingAssets中读取，
//        //正式发布的时候需要大家自行从其他地方读取dll

//        //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//        //这个DLL文件是直接编译HotFix_Project.sln生成的，已经在项目中设置好输出目录为StreamingAssets，在VS里直接编译即可生成到对应目录，无需手动拷贝
//#if UNITY_ANDROID
//        WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.dll");
//#else
//        WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
//#endif
//        while (!www.isDone)
//            yield return null;
//        if (!string.IsNullOrEmpty(www.error))
//            UnityEngine.Debug.LogError(www.error);
//        byte[] dll = www.bytes;
//        www.Dispose();

//        //PDB文件是调试数据库，如需要在日志中显示报错的行号，则必须提供PDB文件，不过由于会额外耗用内存，正式发布时请将PDB去掉，下面LoadAssembly的时候pdb传null即可
//#if UNITY_ANDROID
//        www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb");
//#else
//        www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
//#endif
//        while (!www.isDone)
//            yield return null;
//        if (!string.IsNullOrEmpty(www.error))
//            UnityEngine.Debug.LogError(www.error);
//        byte[] pdb = www.bytes;
//        fs = new MemoryStream(dll);
//        p = new MemoryStream(pdb);
//        try
//        {
//            appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
//        }
//        catch
//        {
//            Debug.LogError("加载热更DLL失败，请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL");
//        }

//        InitializeILRuntime();
//        OnHotFixLoaded();
//    }

//    private void OnDestroy()
//    {
//        fs.Close();
//        p.Close();
//    }

//    unsafe void InitializeILRuntime()
//    {
//#if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
//        //由于Unity的Profiler接口只允许在主线程使用，为了避免出异常，需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
//        appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
//#endif
//        //这里做一些ILRuntime的注册
//        appdomain.RegisterCrossBindingAdaptor(new MonoBehaviourAdapter());
//        appdomain.RegisterValueTypeBinder(typeof(Vector3), new Vector3Binder());
//        //ILRuntime.Runtime.Generated.CLRBindings.Initialize(appdomain);
//    }

//    unsafe void OnHotFixLoaded()
//    {
//        Debug.Log("在热更DLL里面使用MonoBehaviour是可以做到的，但是并不推荐这么做");
//        Debug.Log("因为即便能做到使用，要完全支持MonoBehaviour的所有特性，会需要很多额外的工作量");
//        Debug.Log("而且通过MonoBehaviour做游戏逻辑当项目规模大到一定程度之后会是个噩梦，因此应该尽量避免");

//        Debug.Log("直接调用GameObject.AddComponent<T>会报错，这是因为这个方法是Unity实现的，他并不可能取到热更DLL内部的类型");
//        Debug.Log("因此我们需要挟持AddComponent方法，然后自己实现");
//        Debug.Log("我们先销毁掉之前创建的不合法的MonoBehaviour");
//        SetupCLRRedirection();
//        appdomain.Invoke("HotFix_Project.TestMonoBehaviour", "RunTest", null, gameObject);

//        Debug.Log("可以看到已经成功了");
//        Debug.Log("下面做另外一个实验");
//        Debug.Log("GetComponent跟AddComponent类似，需要我们自己处理");
//        SetupCLRRedirection2();
//        appdomain.Invoke("HotFix_Project.TestMonoBehaviour", "RunTest2", null, gameObject);
//        Debug.Log("成功了");
//        Debug.Log("那我们怎么从Unity主工程获取热更DLL的MonoBehaviour呢？");
//        Debug.Log("这需要我们自己实现一个GetComponent方法");
//        var type = appdomain.LoadedTypes["HotFix_Project.SomeMonoBehaviour2"] as ILType;
//        var smb = GetComponent(type);
//        var m = type.GetMethod("Test2");
//        Debug.Log("现在来试试调用");
//        appdomain.Invoke(m, smb, null);

//        Debug.Log("调用成功！");
//        Debug.Log("我们点一下左边列表里的GameObject，查看一下我们刚刚挂的脚本");
//        Debug.Log("默认情况下是无法显示DLL里面定义的public变量的值的");
//        Debug.Log("这个Demo我们写了一个自定义Inspector来查看变量，同样只是抛砖引玉");
//        Debug.Log("要完整实现MonoBehaviour所有功能得大家自己花功夫了，最好还是避免脚本里使用MonoBehaviour");
//        Debug.Log("具体实现请看MonoBehaviourAdapterEditor");
//        Debug.Log("特别注意，现在仅仅是运行时可以看到和编辑，由于没有处理序列化的问题，所以并不可能保存到Prefab当中，要想实现就得靠大家自己了");
//    }

//    unsafe void SetupCLRRedirection()
//    {
//        //这里面的通常应该写在InitializeILRuntime，这里为了演示写这里
//        var arr = typeof(GameObject).GetMethods();
//        foreach (var i in arr)
//        {
//            if (i.Name == "AddComponent" && i.GetGenericArguments().Length == 1)
//            {
//                appdomain.RegisterCLRMethodRedirection(i, AddComponent);
//            }
//        }

//    }

//    unsafe void SetupCLRRedirection2()
//    {
//        //这里面的通常应该写在InitializeILRuntime，这里为了演示写这里
//        var arr = typeof(GameObject).GetMethods();
//        foreach (var i in arr)
//        {
//            if (i.Name == "GetComponent" && i.GetGenericArguments().Length == 1)
//            {
//                appdomain.RegisterCLRMethodRedirection(i, GetComponent);
//            }
//        }
//    }

//    MonoBehaviourAdapter.Adaptor GetComponent(ILType type)
//    {
//        var arr = GetComponents<MonoBehaviourAdapter.Adaptor>();
//        for (int i = 0; i < arr.Length; i++)
//        {
//            var instance = arr[i];
//            if (instance.ILInstance != null && instance.ILInstance.Type == type)
//            {
//                return instance;
//            }
//        }
//        return null;
//    }

//    unsafe static StackObject* AddComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
//    {
//        //CLR重定向的说明请看相关文档和教程，这里不多做解释
//        ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;

//        var ptr = __esp - 1;
//        //成员方法的第一个参数为this
//        GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
//        if (instance == null)
//            throw new System.NullReferenceException();
//        __intp.Free(ptr);

//        var genericArgument = __method.GenericArguments;
//        //AddComponent应该有且只有1个泛型参数
//        if (genericArgument != null && genericArgument.Length == 1)
//        {
//            var type = genericArgument[0];
//            object res;
//            if (type is CLRType)
//            {
//                //Unity主工程的类不需要任何特殊处理，直接调用Unity接口
//                res = instance.AddComponent(type.TypeForCLR);
//            }
//            else
//            {
//                //热更DLL内的类型比较麻烦。首先我们得自己手动创建实例
//                var ilInstance = new ILTypeInstance(type as ILType, false);//手动创建实例是因为默认方式会new MonoBehaviour，这在Unity里不允许
//                //接下来创建Adapter实例
//                var clrInstance = instance.AddComponent<MonoBehaviourAdapter.Adaptor>();
//                //unity创建的实例并没有热更DLL里面的实例，所以需要手动赋值
//                clrInstance.ILInstance = ilInstance;
//                clrInstance.AppDomain = __domain;
//                //这个实例默认创建的CLRInstance不是通过AddComponent出来的有效实例，所以得手动替换
//                ilInstance.CLRInstance = clrInstance;

//                res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance

//                clrInstance.Awake();//因为Unity调用这个方法时还没准备好所以这里补调一次
//            }

//            return ILIntepreter.PushObject(ptr, __mStack, res);
//        }

//        return __esp;
//    }

//    unsafe static StackObject* GetComponent(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj)
//    {
//        //CLR重定向的说明请看相关文档和教程，这里不多做解释
//        ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;

//        var ptr = __esp - 1;
//        //成员方法的第一个参数为this
//        GameObject instance = StackObject.ToObject(ptr, __domain, __mStack) as GameObject;
//        if (instance == null)
//            throw new System.NullReferenceException();
//        __intp.Free(ptr);

//        var genericArgument = __method.GenericArguments;
//        //AddComponent应该有且只有1个泛型参数
//        if (genericArgument != null && genericArgument.Length == 1)
//        {
//            var type = genericArgument[0];
//            object res = null;
//            if (type is CLRType)
//            {
//                //Unity主工程的类不需要任何特殊处理，直接调用Unity接口
//                res = instance.GetComponent(type.TypeForCLR);
//            }
//            else
//            {
//                //因为所有DLL里面的MonoBehaviour实际都是这个Component，所以我们只能全取出来遍历查找
//                var clrInstances = instance.GetComponents<MonoBehaviourAdapter.Adaptor>();
//                for (int i = 0; i < clrInstances.Length; i++)
//                {
//                    var clrInstance = clrInstances[i];
//                    if (clrInstance.ILInstance != null)//ILInstance为null, 表示是无效的MonoBehaviour，要略过
//                    {
//                        if (clrInstance.ILInstance.Type == type)
//                        {
//                            res = clrInstance.ILInstance;//交给ILRuntime的实例应该为ILInstance
//                            break;
//                        }
//                    }
//                }
//            }

//            return ILIntepreter.PushObject(ptr, __mStack, res);
//        }

//        return __esp;
//    }
//}
